package com.wjf.shortlink.shortlink.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wjf.shortlink.shortlink.common.biz.user.UserContext;
import com.wjf.shortlink.shortlink.common.convention.exception.ServiceException;
import com.wjf.shortlink.shortlink.dao.entity.*;
import com.wjf.shortlink.shortlink.dao.mapper.*;
import com.wjf.shortlink.shortlink.dto.req.ShortLinkGroupStatsAccessRecordReqDTO;
import com.wjf.shortlink.shortlink.dto.req.ShortLinkGroupStatsReqDTO;
import com.wjf.shortlink.shortlink.dto.req.ShortLinkStatsAccessRecordReqDTO;
import com.wjf.shortlink.shortlink.dto.req.ShortLinkStatsReqDTO;
import com.wjf.shortlink.shortlink.dto.resp.*;
import com.wjf.shortlink.shortlink.service.ShortLinkStatsService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

//短链接监控实现层
@Service
@RequiredArgsConstructor
public class ShortLinkStatsServiceImpl implements ShortLinkStatsService {
    private final LinkGroupMapper linkGroupMapper;
    private final LinkAccessStatsMapper linkAccessStatsMapper;
    private final LinkAccessLogsMapper linkAccessLogsMapper;
    private final LinkLocaleStatsMapper linkLocaleStatsMapper;
    private final LinkBrowserStatsMapper linkBrowserStatsMapper;
    private final LinkOsStatsMapper linkOsStatsMapper;
    private final LinkDeviceStatsMapper linkDeviceStatsMapper;
    private final LinkNetworkStatsMapper linkNetworkStatsMapper;

    @Override
    public ShortLinkStatsRespDTO oneShortLinkStats(ShortLinkStatsReqDTO reqDTO) {
        checkGroupBelongToUser(reqDTO.getGid());
        List<LinkAccessStatsDAO> listStatsByShortLink = linkAccessStatsMapper.listStatsByShortLink(reqDTO);
        if (CollUtil.isEmpty(listStatsByShortLink)) {
            return null;
        }
        //基础数据
        LinkAccessStatsDAO linkAccessStatsDAO = linkAccessLogsMapper.findPvUvUipStatsByShortLink(reqDTO);
        //数据详情
        List<ShortLinkStatsAccessDailyRespDTO> dailyRespDTOList = new ArrayList<>();
        List<String> rangeDates = DateUtil.rangeToList(DateUtil.parse(reqDTO.getStartDate()), DateUtil.parse(reqDTO.getEndDate()), DateField.DAY_OF_MONTH)
                .stream().map(DateUtil::formatDate)
                .toList();
        rangeDates.forEach(each -> listStatsByShortLink.stream()
                .filter(item -> Objects.equals(each, DateUtil.formatDate(item.getDate())))
                .findFirst()
                .ifPresentOrElse(item -> {
                    ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                            .date(each)
                            .pv(item.getPv())
                            .uip(item.getUip())
                            .uv(item.getUv())
                            .build();
                    dailyRespDTOList.add(accessDailyRespDTO);
                }, () -> {
                    ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                            .date(each)
                            .pv(0)
                            .uip(0)
                            .uv(0)
                            .build();
                    dailyRespDTOList.add(accessDailyRespDTO);
                })
        );
        //地区访问数据
        List<ShortLinkStatsLocaleCNRespDTO> localeCNRespDTOS = new ArrayList<>();
        List<LinkLocaleStatsDAO> listLocaleByShortLink = linkLocaleStatsMapper.listLocaleByShortLink(reqDTO);
        int localeCnSum = listLocaleByShortLink.stream()
                .mapToInt(LinkLocaleStatsDAO::getCnt)
                .sum();
        listLocaleByShortLink.forEach(item -> {
            double ratio = (double) item.getCnt() / localeCnSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsLocaleCNRespDTO localeCNRespDTO = ShortLinkStatsLocaleCNRespDTO.builder()
                    .cnt(item.getCnt())
                    .locale(item.getProvince())
                    .ratio(actualRatio)
                    .build();
            localeCNRespDTOS.add(localeCNRespDTO);
        });
        //小时访问数据
        List<Integer> hourStats = new ArrayList<>();
        List<LinkAccessStatsDAO> listHourStatsByShortLink = linkAccessStatsMapper.listHourStatsByShortLink(reqDTO);
        for (int i = 0; i < 24; i++) {
            AtomicInteger hour = new AtomicInteger(i);
            int hourCnt = listHourStatsByShortLink.stream()
                    .filter(each -> Objects.equals(each.getHour(), hour.get()))
                    .findFirst()
                    .map(LinkAccessStatsDAO::getPv)
                    .orElse(0);
            hourStats.add(hourCnt);
        }
        //高频ip访问数据
        List<ShortLinkStatsTopIpRespDTO> topIpRespDTOS = new ArrayList<>();
        List<HashMap<String, Object>> listTopIpByShortLink = linkAccessLogsMapper.listTopIpByShortLink(reqDTO);
        listTopIpByShortLink.forEach(each -> {
            ShortLinkStatsTopIpRespDTO topIpRespDTO = ShortLinkStatsTopIpRespDTO.builder()
                    .ip(each.get("ip").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .build();
            topIpRespDTOS.add(topIpRespDTO);
        });
        //星期里的访问数据
        List<Integer> weekdayStats = new ArrayList<>();
        List<LinkAccessStatsDAO> listWeekdayStatsByShortLink = linkAccessStatsMapper.listWeekdayStatsByShortLink(reqDTO);
        for (int i = 1; i < 8; i++) {
            AtomicInteger weekday = new AtomicInteger(i);
            int weekdayCnt = listWeekdayStatsByShortLink.stream()
                    .filter(each -> Objects.equals(each.getWeekday(), weekday.get()))
                    .findFirst()
                    .map(LinkAccessStatsDAO::getWeekday)
                    .orElse(0);
            weekdayStats.add(weekdayCnt);
        }
        //浏览器访问数据
        List<ShortLinkStatsBrowserRespDTO> browserRespDTOS = new ArrayList<>();
        List<HashMap<String, Object>> listBrowserStatsByShortLink = linkBrowserStatsMapper.listBrowserStatsByShortLink(reqDTO);
        int browserSum = listBrowserStatsByShortLink.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listBrowserStatsByShortLink.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / browserSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsBrowserRespDTO browserRespDTO = ShortLinkStatsBrowserRespDTO.builder()
                    .browser(each.get("browser").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .ratio(actualRatio)
                    .build();
            browserRespDTOS.add(browserRespDTO);
        });
        //操作系统访问数据
        List<ShortLinkStatsOsRespDTO> osRespDTOS = new ArrayList<>();
        List<HashMap<String, Object>> listOsStatsByShortLink = linkOsStatsMapper.listOsStatsByShortLink(reqDTO);
        int osSum = listOsStatsByShortLink.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listOsStatsByShortLink.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / osSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsOsRespDTO osRespDTO = ShortLinkStatsOsRespDTO.builder()
                    .os(each.get("os").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .ratio(actualRatio)
                    .build();
        });
        //访客访问数据
        List<ShortLinkStatsUvRespDTO> uvTypeRespDTOS = new ArrayList<>();
        HashMap<String, Object> findUvTypeByShortLink = linkAccessLogsMapper.findUvTypeCntByShortLink(reqDTO);
        int oldUserCnt = Integer.parseInt(Optional.ofNullable(findUvTypeByShortLink)
                .map(each -> each.get("oldUserCnt"))
                .map(Object::toString)
                .orElse("0")
        );
        int newUserCnt = Integer.parseInt(Optional.ofNullable(findUvTypeByShortLink)
                .map(each -> each.get("newUserCnt"))
                .map(Object::toString)
                .orElse("0")
        );
        int uvCnt = oldUserCnt + newUserCnt;
        if (uvCnt == 0) {
            uvCnt = 1;  //保证不是0
        }
        double oldRatio = (double) oldUserCnt / uvCnt;
        double newRatio = (double) newUserCnt / uvCnt;
        double actualOldRatio = Math.round(oldRatio * 100.0) / 100.0;
        double actualNewRatio = Math.round(newRatio * 100.0) / 100.0;
        ShortLinkStatsUvRespDTO oldUvRespDTO = ShortLinkStatsUvRespDTO.builder()
                .cnt(oldUserCnt)
                .ratio(actualOldRatio)
                .uvType("oldUser")
                .build();
        ShortLinkStatsUvRespDTO newUvRespDTO = ShortLinkStatsUvRespDTO.builder()
                .cnt(newUserCnt)
                .ratio(actualNewRatio)
                .uvType("newUser")
                .build();
        uvTypeRespDTOS.add(newUvRespDTO);
        uvTypeRespDTOS.add(oldUvRespDTO);
        //设备访问数据
        List<ShortLinkStatsDeviceRespDTo> deviceRespDTOS = new ArrayList<>();
        List<LinkDeviceStatsDAO> listDeviceStatsByShortLink = linkDeviceStatsMapper.listDeviceStatsByShortLink(reqDTO);
        int deviceSum = listDeviceStatsByShortLink.stream()
                .mapToInt(LinkDeviceStatsDAO::getCnt)
                .sum();
        listDeviceStatsByShortLink.forEach(each -> {
            double ratio = (double) each.getCnt() / deviceSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsDeviceRespDTo deviceRespDTo = ShortLinkStatsDeviceRespDTo.builder()
                    .cnt(each.getCnt())
                    .device(each.getDevice())
                    .ratio(actualRatio)
                    .build();
            deviceRespDTOS.add(deviceRespDTo);
        });
        //网络访问数据
        List<ShortLinkStatsNetworkRespDTO> networkRespDTOS = new ArrayList<>();
        List<LinkNetworkStatsDAO> listNetworkStatsByStatsLink = linkNetworkStatsMapper.listNetworkStatsByShortLink(reqDTO);
        int networkSum = listNetworkStatsByStatsLink.stream()
                .mapToInt(LinkNetworkStatsDAO::getCnt)
                .sum();
        listNetworkStatsByStatsLink.forEach(each -> {
            double ratio = (double) each.getCnt() / networkSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsNetworkRespDTO networkRespDTO = ShortLinkStatsNetworkRespDTO.builder()
                    .cnt(each.getCnt())
                    .network(each.getNetwork())
                    .ratio(actualRatio)
                    .build();
            networkRespDTOS.add(networkRespDTO);
        });
        return ShortLinkStatsRespDTO.builder()
                .pv(linkAccessStatsDAO.getPv())
                .uv(linkAccessStatsDAO.getUv())
                .uip(linkAccessStatsDAO.getUip())
                .daily(dailyRespDTOList)
                .localeCnStats(localeCNRespDTOS)
                .hourStats(hourStats)
                .topIpStats(topIpRespDTOS)
                .weekdayStats(weekdayStats)
                .browserStats(browserRespDTOS)
                .osStats(osRespDTOS)
                .uvTypeSTats(uvTypeRespDTOS)
                .deviceStats(deviceRespDTOS)
                .networkStats(networkRespDTOS)
                .build();
    }

    //获取分组短链接监控数据
    @Override
    public ShortLinkStatsRespDTO groupShortLinkStats(ShortLinkGroupStatsReqDTO reqDTO) {
        //检查用户登录和gid是否属于该登录用户
        checkGroupBelongToUser(reqDTO.getGid());
        List<LinkAccessStatsDAO> listStatsByGroup = linkAccessStatsMapper.listStatsByGroup(reqDTO);
        if (CollUtil.isEmpty(listStatsByGroup)) {
            return null;
        }
        //基础访问数据
        LinkAccessStatsDAO pvUvUipStatsByGroup = linkAccessLogsMapper.findPvUvUipStatsByGroup(reqDTO);

        List<ShortLinkStatsAccessDailyRespDTO> daily = new ArrayList<>();
        List<String> rangeDates = DateUtil.rangeToList(DateUtil.parse(reqDTO.getStartDate()), DateUtil.parse(reqDTO.getEndDate()), DateField.DAY_OF_MONTH).stream()
                .map(DateUtil::formatDate)
                .toList();
        rangeDates.forEach(each -> listStatsByGroup.stream()
                .filter(item -> Objects.equals(each, DateUtil.formatDate(item.getDate())))
                .findFirst()
                .ifPresentOrElse(item -> {
                    ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                            .date(each)
                            .uv(item.getUv())
                            .pv(item.getPv())
                            .uip(item.getUip())
                            .build();
                    daily.add(accessDailyRespDTO);
                }, () -> {
                    ShortLinkStatsAccessDailyRespDTO accessDailyRespDTO = ShortLinkStatsAccessDailyRespDTO.builder()
                            .date(each)
                            .uv(0)
                            .pv(0)
                            .uip(0)
                            .build();
                    daily.add(accessDailyRespDTO);
                }));
        //地区访问数据
        List<ShortLinkStatsLocaleCNRespDTO> localeCNRespDTOS = new ArrayList<>();
        List<LinkLocaleStatsDAO> listedLocaleByGroup = linkLocaleStatsMapper.listLocaleByGroup(reqDTO);
        int localeCnSum = listedLocaleByGroup.stream()
                .mapToInt(LinkLocaleStatsDAO::getCnt)
                .sum();
        listedLocaleByGroup.forEach(each -> {
            double ratio = (double) each.getCnt() / localeCnSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsLocaleCNRespDTO localeCNRespDTO = ShortLinkStatsLocaleCNRespDTO.builder()
                    .ratio(actualRatio)
                    .cnt(each.getCnt())
                    .locale(each.getProvince())
                    .build();
            localeCNRespDTOS.add(localeCNRespDTO);
        });
        //小时访问数据
        List<Integer> hourStats = new ArrayList<>();
        List<LinkAccessStatsDAO> listHourStatsByGroup = linkAccessStatsMapper.listHourStatsByGroup(reqDTO);
        for (int i = 0; i < 24; i++) {
            AtomicInteger hour = new AtomicInteger(i);
            int hourCnt = listHourStatsByGroup.stream()
                    .filter(each -> Objects.equals(each.getHour(), hour.get()))
                    .findFirst()
                    .map(LinkAccessStatsDAO::getPv)
                    .orElse(0);
            hourStats.add(hourCnt);
        }
        //高频访问ip数据
        List<ShortLinkStatsTopIpRespDTO> topIpRespDTOS = new ArrayList<>();
        List<HashMap<String, Object>> listTopIpByGroup = linkAccessLogsMapper.listTopIpByGroup(reqDTO);
        listTopIpByGroup.forEach(each -> {
            ShortLinkStatsTopIpRespDTO topIpRespDTO = ShortLinkStatsTopIpRespDTO.builder()
                    .ip(each.get("ip").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .build();
            topIpRespDTOS.add(topIpRespDTO);
        });
        //一周访问数据
        List<Integer> weekdayStats = new ArrayList<>();
        List<LinkAccessStatsDAO> listWeekdayStatsByGroup = linkAccessStatsMapper.listWeekdayStatsByGroup(reqDTO);
        for (int i = 0; i < 8; i++) {
            AtomicInteger weekday = new AtomicInteger(i);
            int weekdayCnt = listWeekdayStatsByGroup.stream()
                    .filter(each -> Objects.equals(each.getWeekday(), weekday.get()))
                    .findFirst()
                    .map(LinkAccessStatsDAO::getPv)
                    .orElse(0);
            weekdayStats.add(weekdayCnt);
        }
        //浏览器访问数据
        List<ShortLinkStatsBrowserRespDTO> browserRespDTOS = new ArrayList<>();
        List<HashMap<String, Object>> listBrowserStatsByGroup = linkBrowserStatsMapper.listBrowserStatsByGroup(reqDTO);
        int browserSum = listBrowserStatsByGroup.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listBrowserStatsByGroup.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / browserSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsBrowserRespDTO browserRespDTO = ShortLinkStatsBrowserRespDTO.builder()
                    .ratio(actualRatio)
                    .browser(each.get("browser").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .build();
            browserRespDTOS.add(browserRespDTO);
        });
        //操作系统访问数据
        List<ShortLinkStatsOsRespDTO> osRespDTOS = new ArrayList<>();
        List<HashMap<String, Object>> listOsStatsByGroup = linkOsStatsMapper.listOsStatsByGroup(reqDTO);
        int osSum = listOsStatsByGroup.stream()
                .mapToInt(each -> Integer.parseInt(each.get("count").toString()))
                .sum();
        listOsStatsByGroup.forEach(each -> {
            double ratio = (double) Integer.parseInt(each.get("count").toString()) / osSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsOsRespDTO osRespDTO = ShortLinkStatsOsRespDTO.builder()
                    .os(each.get("os").toString())
                    .cnt(Integer.parseInt(each.get("count").toString()))
                    .ratio(actualRatio)
                    .build();
            osRespDTOS.add(osRespDTO);
        });
        //设备访问数据
        List<ShortLinkStatsDeviceRespDTo> deviceRespDTOS = new ArrayList<>();
        List<LinkDeviceStatsDAO> listDeviceStatsByGroup = linkDeviceStatsMapper.listDeviceStatsByGroup(reqDTO);
        int deviceSum = listDeviceStatsByGroup.stream()
                .mapToInt(LinkDeviceStatsDAO::getCnt)
                .sum();
        listDeviceStatsByGroup.forEach(each -> {
            double ratio = (double) each.getCnt() / deviceSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsDeviceRespDTo deviceRespDTo = ShortLinkStatsDeviceRespDTo.builder()
                    .ratio(actualRatio)
                    .device(each.getDevice())
                    .cnt(each.getCnt())
                    .build();
            deviceRespDTOS.add(deviceRespDTo);
        });
        // 网络访问数据
        List<ShortLinkStatsNetworkRespDTO> networkRespDTOS = new ArrayList<>();
        List<LinkNetworkStatsDAO> listNetworkStatsByGroup = linkNetworkStatsMapper.listNetworkStatsByGroup(reqDTO);
        int networkSum = listNetworkStatsByGroup.stream()
                .mapToInt(LinkNetworkStatsDAO::getCnt)
                .sum();
        listNetworkStatsByGroup.forEach(each -> {
            double ratio = (double) each.getCnt() / networkSum;
            double actualRatio = Math.round(ratio * 100.0) / 100.0;
            ShortLinkStatsNetworkRespDTO networkRespDTO = ShortLinkStatsNetworkRespDTO.builder()
                    .network(each.getNetwork())
                    .cnt(each.getCnt())
                    .ratio(actualRatio)
                    .build();
            networkRespDTOS.add(networkRespDTO);
        });
        return ShortLinkStatsRespDTO.builder()
                .pv(pvUvUipStatsByGroup.getPv())
                .uv(pvUvUipStatsByGroup.getUv())
                .uip(pvUvUipStatsByGroup.getUip())
                .daily(daily)
                .localeCnStats(localeCNRespDTOS)
                .hourStats(hourStats)
                .topIpStats(topIpRespDTOS)
                .weekdayStats(weekdayStats)
                .browserStats(browserRespDTOS)
                .osStats(osRespDTOS)
                .deviceStats(deviceRespDTOS)
                .networkStats(networkRespDTOS)
                .build();
    }

    //检查用户登录和分组
    public void checkGroupBelongToUser(String gid) throws ServiceException {
        String username = Optional.ofNullable(UserContext.getUserName())
                .orElseThrow(() -> new ServiceException("用户未登录"));
        LambdaQueryWrapper<GroupDAO> queryWrapper = Wrappers.lambdaQuery(GroupDAO.class)
                .eq(GroupDAO::getGid, gid)
                .eq(GroupDAO::getUsername, username);
        List<GroupDAO> groupDAOList = linkGroupMapper.selectList(queryWrapper);
        if (CollUtil.isEmpty(groupDAOList)) {
            throw new ServiceException("用户信息与分组标识不匹配");
        }
    }

    //获取单个短链接指定时间内分页监控数据
    @Override
    public IPage<ShortLinkStatsAccessRecordRespDTO> shortLinkStatsAccessRecord(ShortLinkStatsAccessRecordReqDTO reqDTO) {
        checkGroupBelongToUser(reqDTO.getGid());

        LambdaQueryWrapper<LinkAccessLogsDAO> queryWrapper = Wrappers.lambdaQuery(LinkAccessLogsDAO.class)
                .eq(LinkAccessLogsDAO::getFullShortUrl, reqDTO.getFullShortUrl())
                .eq(LinkAccessLogsDAO::getDelFlag, 0)
                .between(LinkAccessLogsDAO::getCreateTime, reqDTO.getStartDate(), reqDTO.getEndDate())
                .orderByDesc(LinkAccessLogsDAO::getCreateTime);
        IPage<LinkAccessLogsDAO> linkAccessLogsDAOIPage = linkAccessLogsMapper.selectPage(reqDTO, queryWrapper);
        if (CollUtil.isEmpty(linkAccessLogsDAOIPage.getRecords())) {
            return new Page<>();
        }
        IPage<ShortLinkStatsAccessRecordRespDTO> actualResult = linkAccessLogsDAOIPage.convert(each -> {
            return BeanUtil.toBean(each, ShortLinkStatsAccessRecordRespDTO.class);
        });
        List<String> userAccessLogsList = actualResult.getRecords().stream()
                .map(ShortLinkStatsAccessRecordRespDTO::getUser)
                .toList();
        List<Map<String, Object>> uvTypeList = linkAccessLogsMapper.selectUvTypeByUsers(
                reqDTO.getGid(),
                reqDTO.getFullShortUrl(),
                reqDTO.getEnableStats(),
                reqDTO.getStartDate(),
                reqDTO.getEndDate(),
                userAccessLogsList
        );
        actualResult.getRecords().forEach(each -> {
            String uvType = uvTypeList.stream()
                    .filter(item -> Objects.equals(each.getUser(), item.get("user")))
                    .findFirst()
                    .map(item -> item.get("uvType"))
                    .map(Object::toString)
                    .orElse("旧访客");
            each.setUvType(uvType);
        });
        return actualResult;
    }

    //获取分组短链接指定时间内分页监控数据
    @Override
    public IPage<ShortLinkStatsAccessRecordRespDTO> groupShortLinkStatsAccessRecord(ShortLinkGroupStatsAccessRecordReqDTO reqDTO) {

        checkGroupBelongToUser(reqDTO.getGid());
        IPage<LinkAccessLogsDAO> linkAccessLogsDAOIPage = linkAccessLogsMapper.selectGroupPage(reqDTO);
        if (CollUtil.isEmpty(linkAccessLogsDAOIPage.getRecords())) {
            return new Page<>();
        }
        IPage<ShortLinkStatsAccessRecordRespDTO> actualResult = linkAccessLogsDAOIPage.convert(each -> {
            return BeanUtil.toBean(each, ShortLinkStatsAccessRecordRespDTO.class);
        });
        List<String> userAccessLogsList = actualResult.getRecords().stream()
                .map(ShortLinkStatsAccessRecordRespDTO::getUser)
                .toList();
        List<Map<String, Object>> uvTypeList = linkAccessLogsMapper.selectGroupUvTypeByUsers(
                reqDTO.getGid(),
                reqDTO.getStartDate(),
                reqDTO.getEndDate(),
                userAccessLogsList
        );
        actualResult.getRecords().forEach(each -> {
            String uvType = uvTypeList.stream()
                    .filter(item -> Objects.equals(each.getUser(), item.get("user")))
                    .findFirst()
                    .map(item -> item.get("uvType"))
                    .map(Object::toString)
                    .orElse("旧访客");
            each.setUvType(uvType);
        });
        return actualResult;
    }
}

